﻿using System;

public class ChannelDM : PapuChannel
{
    internal int dacLsb;
    internal int data;
    internal int deltaCounter;
    internal int dmaCounter;
    internal int dmaFrequency;
    internal bool hasSample;
    internal bool irqGenerated;
    internal bool isEnabled_Renamed_Field;
    public const int MODE_IRQ = 2;
    public const int MODE_LOOP = 1;
    public const int MODE_NORMAL = 0;
    internal PAPU papu;
    internal int playAddress;
    internal int playLength;
    internal int playLengthCounter;
    internal int playMode;
    internal int playStartAddress;
    internal int reg4012;
    internal int reg4013;
    internal int sample;
    internal int shiftCounter;
    internal int status;

    public ChannelDM(PAPU papu)
    {
        this.papu = papu;
    }

    public virtual void clockDmc()
    {
        if (this.hasSample)
        {
            if ((this.data & 1) == 0)
            {
                if (this.deltaCounter > 0)
                {
                    this.deltaCounter--;
                }
            }
            else if (this.deltaCounter < 0x3f)
            {
                this.deltaCounter++;
            }
            this.sample = this.isEnabled_Renamed_Field ? ((this.deltaCounter << 1) + this.dacLsb) : 0;
            this.data = this.data >> 1;
        }
        this.dmaCounter--;
        if (this.dmaCounter <= 0)
        {
            this.hasSample = false;
            this.endOfSample();
            this.dmaCounter = 8;
        }
        if (this.irqGenerated)
        {
            this.papu.nes.cpu.requestIrq(0);
        }
    }

    public virtual void destroy()
    {
        this.papu = null;
    }

    private void endOfSample()
    {
        if ((this.playLengthCounter == 0) && (this.playMode == 1))
        {
            this.playAddress = this.playStartAddress;
            this.playLengthCounter = this.playLength;
        }
        if (this.playLengthCounter > 0)
        {
            this.nextSample();
            if ((this.playLengthCounter == 0) && (this.playMode == 2))
            {
                this.irqGenerated = true;
            }
        }
    }

    private void nextSample()
    {
        this.data = this.papu.Nes.MemoryMapper.load(this.playAddress);
        this.papu.Nes.cpu.haltCycles(4);
        this.playLengthCounter--;
        this.playAddress++;
        if (this.playAddress > 0xffff)
        {
            this.playAddress = 0x8000;
        }
        this.hasSample = true;
    }

    public virtual void reset()
    {
        this.isEnabled_Renamed_Field = false;
        this.irqGenerated = false;
        this.playMode = 0;
        this.dmaFrequency = 0;
        this.dmaCounter = 0;
        this.deltaCounter = 0;
        this.playStartAddress = 0;
        this.playAddress = 0;
        this.playLength = 0;
        this.playLengthCounter = 0;
        this.status = 0;
        this.sample = 0;
        this.dacLsb = 0;
        this.shiftCounter = 0;
        this.reg4012 = 0;
        this.reg4013 = 0;
        this.data = 0;
    }

    public virtual void writeReg(int address, int value_Renamed)
    {
        if (address == 0x4010)
        {
            if ((value_Renamed >> 6) == 0)
            {
                this.playMode = 0;
            }
            else if (((value_Renamed >> 6) & 1) == 1)
            {
                this.playMode = 1;
            }
            else if ((value_Renamed >> 6) == 2)
            {
                this.playMode = 2;
            }
            if ((value_Renamed & 0x80) == 0)
            {
                this.irqGenerated = false;
            }
            this.dmaFrequency = this.papu.getDmcFrequency(value_Renamed & 15);
        }
        else if (address == 0x4011)
        {
            this.deltaCounter = (value_Renamed >> 1) & 0x3f;
            this.dacLsb = value_Renamed & 1;
            if (this.papu.userEnableDmc)
            {
                this.sample = (this.deltaCounter << 1) + this.dacLsb;
            }
        }
        else if (address == 0x4012)
        {
            this.playStartAddress = (value_Renamed << 6) | 0xc000;
            this.playAddress = this.playStartAddress;
            this.reg4012 = value_Renamed;
        }
        else if (address == 0x4013)
        {
            this.playLength = (value_Renamed << 4) + 1;
            this.playLengthCounter = this.playLength;
            this.reg4013 = value_Renamed;
        }
        else if (address == 0x4015)
        {
            if (((value_Renamed >> 4) & 1) == 0)
            {
                this.playLengthCounter = 0;
            }
            else
            {
                this.playAddress = this.playStartAddress;
                this.playLengthCounter = this.playLength;
            }
            this.irqGenerated = false;
        }
    }

    public virtual bool Enabled
    {
        get
        {
            return this.isEnabled_Renamed_Field;
        }
        set
        {
            if (!this.isEnabled_Renamed_Field && value)
            {
                this.playLengthCounter = this.playLength;
            }
            this.isEnabled_Renamed_Field = value;
        }
    }

    public virtual int IrqStatus
    {
        get
        {
            if (!this.irqGenerated)
            {
                return 0;
            }
            return 1;
        }
    }

    public virtual int LengthStatus
    {
        get
        {
            if ((this.playLengthCounter != 0) && this.isEnabled_Renamed_Field)
            {
                return 1;
            }
            return 0;
        }
    }
}

